package com.cskaoyan.javase.object._3equals;

import java.lang.management.GarbageCollectorMXBean;
import java.util.TreeMap;

/**
 * equals方法
 *      1.方法的声明:
 *      public boolean equals(Object obj)
 *      没有特别需要注意的地方,但是注意方法有返回值,返回一个布尔值
 *      并且方法需要一个参数,参数的类型是Object,这意味只要是对象就能够传入该方法
 *
 *      2.方法的作用:
 *      equals是相等的意思,该方法需要一个对象来调用它,并且需要传入另一个对象
 *      作用就是比较方法的调用者对象和方法传入的对象,是否相等
 *      相等返回true,不相等返回false
 *
 *      3.对象如何判断相等呢?
 *          需要一个判断的依据,该方法的实现(方法体)就是判断对象相等的条件
 *
 *      4.Object类当中默认的判断相等的条件:
 *          return (this == obj);
 *       这个判断相等条件是很苛刻的,只有当两个引用完全指向同一个对象时,对象才相等.
 *       这个条件在实际开发中过于苛刻了,我们不需要,需要重写该方法.
 *
 *      5.我们认为的对象相等:
 *          a.如果两个对象完全不是同种类型对象,那就没有可比性,直接判定为不相等.
 *          b.如果两个对象是同种类型对象,那就可以比一比了,如果对象中属性的取值是一样的,那么就可以认定对象相等.
 *
 *      6.equals方法的重写,jdk文档中是有规定的,需要满足以下条件:
 *          a.自反性：对于任何非空引用值 x，x.equals(x)都应返回 true
 *
 *
 *          b.排他性：当比对的不是同种类型的对象或者是一个null时，默认返回false
 *
 *          c.对称性：对于任何非空引用值 x 和 y，当且仅当y.equals(x) 返回 true 时，x.equals(y) 才应返回true
 *          a.equals(b)
 *          和
 *          b.equals(a)
 *          结果应该是一样的
 *
 *          d.传递性：对于任何非空引用值 x、y 和 z，如果x.equals(y)返回 true，并且 y.equals(z) 返回 true，那么x.equals(z) 应返回 true。
 *          a = b
 *          b = c
 *          -->
 *          a = c
 *
 *          e.一致性：对于任何非空引用值 x 和 y，多次调用 x.equals(y)始终返回 true 或始终返回 false。
 *          只要判断相等的比较条件不变
 *          方法的执行结果也不变
 *
 * 以上,只要用对象的属性取值来判断对象相等,就能够自动满足对称性,传递性和一致性.
 *
 * 注意:
 *      1.如果不是有特殊需求,建议重写equals方法,使用快捷键alt + insert
 *      2.浮点型比较大小,在绝大多数实际场景下,使用"=="是完全可以的.但是浮点数毕竟有特殊的取值,比如无穷大,无穷小,非数(NaN)等概念
 *      所以为了程序严谨考虑
 *      建议比较浮点数的大小不要用双等号
 *      而是使用包装类型.compare()方法:
 *          Float.compare(float a,float b)
 *          Double.compare(double a,doublet b)
 *      这个方法可以看成 a-b
 *      当方法返回小于0的数时,说明a<b
 *      当方法返回大于0的数时,说明a>b
 *      当方法返回等于0的数时,说明a=b
 *
 *      3.如果类中有引用数据类型成员变量，需要去调用它们的equals方法完成比较。这就意味着还需要重写这个类的equals方法。
 *
 *      4.BigDecimal
 *
 * @since 11:39
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        Student s1 = new Student(18, 20);
        Student s2 = new Student(28, 10);
        Student s3 = new Student(18, 20);
        System.out.println(s1 == s2);
        System.out.println(s1.equals(s2));
        System.out.println(s1.equals(s3));
        Teacher t = new Teacher(18, 20);
        System.out.println(s1.equals(t));
    }
}

class Student {
    int age;
    double salary;

    public Student(int age, double salary) {
        this.age = age;
        this.salary = salary;
    }

    // 重写equals方法
    /*@Override
    public boolean equals(Object obj) {
        // 1.自己和自己比较,返回true
        if (this == obj) {
            return true;
        }

        // 2.排他性,排除null和非同种类型对象
        // 先判断null,再判断非同种类型对象
        // 下面是固定格式
        if (obj == null || (this.getClass() != obj.getClass())) {
            return false;
        }
        // 3.只要用对象的属性取值来判断对象相等,就能够自动满足对称性,传递性和一致性.
        // 由于上面已经getClass判断过了,如果不是同种类型,已经return false了.所以代码运行到这里,obj一定是Student类的对象
        Student target = (Student) obj;
        *//*if (this.age == target.age){
            if (this.salary == target.salary){
                return true;
            }
        }
        return false;*//*
        if ((this.age != target.age)) {
            return false;
        }
        return this.salary == target.salary;
    }*/

    /*@Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Student)) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return Double.compare(student.salary, salary) == 0;
    }*/

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return Double.compare(student.salary, salary) == 0;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = age;
        temp = Double.doubleToLongBits(salary);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }
}

class Teacher {
    int age;
    double salary;

    public Teacher(int age, double salary) {
        this.age = age;
        this.salary = salary;
    }
}